package com.starrk.myblog.foreground.config.shiro;

import com.starrk.myblog.foreground.mapper.properties.RedisProperties;
import org.apache.shiro.authc.credential.HashedCredentialsMatcher;
import org.apache.shiro.codec.Base64;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.session.mgt.eis.SessionIdGenerator;
import org.apache.shiro.spring.LifecycleBeanPostProcessor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.CookieRememberMeManager;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.apache.shiro.web.servlet.SimpleCookie;
import org.apache.shiro.web.session.mgt.DefaultWebSessionManager;
import org.springframework.boot.web.servlet.FilterRegistrationBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.DependsOn;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.web.filter.DelegatingFilterProxy;


import javax.servlet.DispatcherType;
import javax.servlet.Filter;
import java.util.LinkedHashMap;
import java.util.Map;

@Configuration
public class ShiroConfig {

  @Bean
  public RedisProperties shiroProperties() {
    return new RedisProperties();
  }

  /**
   * FilterRegistrationBean
   */
  @Bean
  public FilterRegistrationBean filterRegistrationBean() {
    FilterRegistrationBean filterRegistration = new FilterRegistrationBean();
    filterRegistration.setFilter(new DelegatingFilterProxy("shiroFilter"));
    filterRegistration.setEnabled(true);
    filterRegistration.addUrlPatterns("/*");
    filterRegistration.setDispatcherTypes(DispatcherType.REQUEST);
    return filterRegistration;
  }

  /**
   * 过滤器及映射路径的配置
   */
  @Bean(name = "shiroFilter")
  public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager,
      RedisTemplate redisTemplate) {
    ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
    shiroFilterFactoryBean.setSecurityManager(securityManager);
    shiroFilterFactoryBean.setLoginUrl("/signin.html");
    shiroFilterFactoryBean.setSuccessUrl("/index.html");
    shiroFilterFactoryBean.setUnauthorizedUrl("/403.html");

    Map<String, Filter> filters = new LinkedHashMap<String, Filter>();
//    filters.put("perms", urlPermissionsFilter());
    filters.put("logout", new MySignOutFilter(redisTemplate));
    shiroFilterFactoryBean.setFilters(filters);

    Map<String, String> filterChainDefinitionMap = new LinkedHashMap<String, String>();
    filterChainDefinitionMap.put("/static/**", "anon");
    filterChainDefinitionMap.put("/403.html", "anon");
    filterChainDefinitionMap.put("/signin.html", "anon");
    filterChainDefinitionMap.put("/signout", "logout");
    filterChainDefinitionMap.put("/**/delete*/**", "perms[Archer]");
    filterChainDefinitionMap.put("/**/select*/**", "perms[Saber]");
    filterChainDefinitionMap.put("/**/find*/**", "perms[Saber]");
    filterChainDefinitionMap.put("/**/update*", "perms[Lancer]");
//        filterChainDefinitionMap.put("/**/insert*", "perms[Berserker]");
    filterChainDefinitionMap.put("/regist.html", "anon");
    filterChainDefinitionMap.put("/contact.html", "authc,roles[天下无双]");
    filterChainDefinitionMap.put("/post.html", "user,roles[Master]");
    filterChainDefinitionMap.put("/about.html", "user");

    shiroFilterFactoryBean.setFilterChainDefinitionMap(filterChainDefinitionMap);
    return shiroFilterFactoryBean;

  }

  /**
   * 安全管理模块，所有的manager在此配置
   * @param redisTemplate
   * @return
   */
  @Bean(name = "securityManager")
  public SecurityManager securityManager(RedisTemplate redisTemplate) {
    DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();

    //自定义realm
    securityManager.setRealm(myShiroRealm(redisTemplate));

    //自定义session管理 使用redis
    securityManager.setSessionManager(sessionManager(redisTemplate));

//    //自定义缓存实现 使用redis
//    securityManager.setCacheManager(redisCacheManager());

    //注入记住我管理器;
    securityManager.setRememberMeManager(rememberMeManager());

    return securityManager;
  }

  /**
   * 认证与授权模块
   * @param redisTemplate
   * @return
   */
  @Bean(name = "myShiroRealm")
  @DependsOn(value = {"lifecycleBeanPostProcessor", "ShiroRedisCacheManager"})
  public MyShiroRealm myShiroRealm(RedisTemplate redisTemplate) {
    MyShiroRealm shiroRealm = new MyShiroRealm();
    shiroRealm.setCacheManager(redisCacheManager(redisTemplate));
    shiroRealm.setCachingEnabled(true);
    //设置认证密码算法及迭代复杂度
    shiroRealm.setCredentialsMatcher(credentialsMatcher());
    //认证
    shiroRealm.setAuthenticationCachingEnabled(false);
    //授权
    shiroRealm.setAuthorizationCachingEnabled(false);
    return shiroRealm;
  }

  /**
   * realm的认证算法
   * @return
   */
  @Bean(name = "hashedCredentialsMatcher")
  public HashedCredentialsMatcher credentialsMatcher() {
    HashedCredentialsMatcher credentialsMatcher = new HashedCredentialsMatcher("md5");
    credentialsMatcher.setHashIterations(2);
    credentialsMatcher.setStoredCredentialsHexEncoded(true);
    return credentialsMatcher;
  }


  /**
   * 缓存管理器的配置
   * @param redisTemplate
   * @return
   */
  @Bean(name = "ShiroRedisCacheManager")
  public ShiroRedisCacheManager redisCacheManager(RedisTemplate redisTemplate) {
    ShiroRedisCacheManager redisCacheManager = new ShiroRedisCacheManager(redisTemplate);
    //name是key的前缀，可以设置任何值，无影响，可以设置带项目特色的值
    redisCacheManager.createCache("shiro_redis");
    return redisCacheManager;
  }

//    @Bean
//    public URLPermissionsFilter urlPermissionsFilter() {
//        return new URLPermissionsFilter();
//    }

  /**
   *  配置sessionmanager，由redis存储数据
   */
  @Bean(name = "sessionManager")
  @DependsOn(value = "lifecycleBeanPostProcessor")
  public DefaultWebSessionManager sessionManager(RedisTemplate redisTemplate) {
    DefaultWebSessionManager sessionManager = new DefaultWebSessionManager();
    MyRedisSessionDao redisSessionDao = new MyRedisSessionDao(redisTemplate);
    //这个name的作用也不大，只是有特色的cookie的名称。
    redisSessionDao.setSessionIdGenerator(sessionIdGenerator("starrkCookie"));
    sessionManager.setSessionDAO(redisSessionDao);
    sessionManager.setDeleteInvalidSessions(true);
    SimpleCookie cookie = new SimpleCookie();
    cookie.setName("starrkCookie");
    sessionManager.setSessionIdCookie(cookie);
    sessionManager.setSessionIdCookieEnabled(true);
    return sessionManager;
  }

  /**
   * 自定义的SessionId生成器
   * @param name
   * @return
   */
  public MySessionIdGenerator sessionIdGenerator(String name) {
    return new MySessionIdGenerator(name);
  }

  /**
   * 这个参数是RememberMecookie的名称，随便起。
   * remenberMeCookie是一个实现了将用户名保存在客户端的一个cookie，与登陆时的cookie是两个simpleCookie。
   * 登陆时会根据权限去匹配，如是user权限，则不会先去认证模块认证，而是先去搜索cookie中是否有rememberMeCookie，
   * 如果存在该cookie，则可以绕过认证模块，直接寻找授权模块获取角色权限信息。
   * 如果权限是authc,则仍会跳转到登陆页面去进行登陆认证.
   * @return
   */
  public SimpleCookie rememberMeCookie() {
    SimpleCookie simpleCookie = new SimpleCookie("remenbermeCookie");
    //<!-- 记住我cookie生效时间30天 ,单位秒;-->
    simpleCookie.setMaxAge(60);
    return simpleCookie;
  }

  /**
   * cookie管理对象;记住我功能
   */
  public CookieRememberMeManager rememberMeManager() {
    CookieRememberMeManager cookieRememberMeManager = new CookieRememberMeManager();
    cookieRememberMeManager.setCookie(rememberMeCookie());
    //rememberMe cookie加密的密钥 建议每个项目都不一样 默认AES算法 密钥长度(128 256 512 位)
    cookieRememberMeManager.setCipherKey(Base64.decode("3AvVhmFLUs0KTA3Kprsdag=="));
    return cookieRememberMeManager;
  }

  /**
   * shiro管理生命周期的东西
   * @return
   */
  @Bean(name = "lifecycleBeanPostProcessor")
  public LifecycleBeanPostProcessor lifecycleBeanPostProcessor() {
    return new LifecycleBeanPostProcessor();
  }
}
